package bjut.core.aop;

import bjut.core.aware.BeanFactoryAware;
import bjut.core.exception.BeansException;
import bjut.core.factory.BeanFactory;
import bjut.core.factory.DefaultListableBeanFactory;
import bjut.core.postprocessor.InstantiationAwareBeanPostProcessor;
import bjut.core.postprocessor.SmartInstantiationAwareBeanPostProcessor;
import bjut.core.property.PropertyValues;
import bjut.core.proxy.ProxyFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * @Author: zhuzhiming
 * @Date: 2022/6/28 19:42
 */
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    private final Set<Object> earlyProxyReference = Collections.synchronizedSet(new HashSet<>());

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return pvs;
    }

    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
    }

    @Override
    public Object postBeanPostProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }


    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        earlyProxyReference.add(beanName);
        //看是否需要提前生成代理对象
        return wrapIfNecessary(bean, beanName);
    }


    @Override
    public Object postBeanPostProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if (!earlyProxyReference.contains(beanName)) {
            // 看是否需要生成代理对象
            return wrapIfNecessary(bean, beanName);
        }

        return bean;
    }


    protected Object wrapIfNecessary(Object bean, String beanName) {

        if (isInfrastructureClass(bean.getClass())) {
            return bean;
        }

        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

        for (AspectJExpressionPointcutAdvisor advisor : advisors) {

            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
           //先看类是否符合
            if (!classFilter.matches(bean.getClass())) {
                continue;
            }

            AdvisedSupport advisedSupport = new AdvisedSupport();
            TargetSource targetSource = new TargetSource(bean);
            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            //在看方法是否符合
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(false);

            return new ProxyFactory(advisedSupport).getProxy();
        }
        return bean;
    }
}
